001 /*
002 * Copyright 2003-2005 The Apache Software Foundation
003 * Copyright 2005 Stephen McConnell
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package net.dpml.cli.validation;
018
019 import java.util.List;
020 import java.util.ListIterator;
021
022 import net.dpml.cli.resource.ResourceConstants;
023 import net.dpml.cli.resource.ResourceHelper;
024
025 /**
026 * The <code>ClassValidator</code> validates the string argument
027 * values are class names.
028 *
029 * The following example shows how to validate the 'logger'
030 * argument value is a class name, that can be instantiated.
031 *
032 * <pre>
033 * ...
034 * ClassValidator validator = new ClassValidator();
035 * validator.setInstance(true);
036 *
037 * ArgumentBuilder builder = new ArgumentBuilder();
038 * Argument logger =
039 * builder.withName("logger");
040 * .withValidator(validator);
041 * </pre>
042 *
043 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
044 * @version 1.0.0
045 */
046 public class ClassValidator implements Validator
047 {
048 /** i18n */
049 private static final ResourceHelper RESOURCES =
050 ResourceHelper.getResourceHelper();
051
052 /** whether the class argument is loadable */
053 private boolean m_loadable;
054
055 /** whether to create an instance of the class */
056 private boolean m_instance;
057
058 /** the classloader to load classes from */
059 private ClassLoader m_loader;
060
061 /**
062 * Validate each argument value in the specified List against this instances
063 * permitted attributes.
064 *
065 * If a value is valid then it's <code>String</code> value in the list is
066 * replaced with it's <code>Class</code> value or instance.
067 *
068 * @param values the list of values to validate
069 * @see net.dpml.cli.validation.Validator#validate(java.util.List)
070 * @exception InvalidArgumentException if a value is invalid
071 */
072 public void validate( final List values ) throws InvalidArgumentException
073 {
074 for( final ListIterator i = values.listIterator(); i.hasNext();)
075 {
076 final String name = (String) i.next();
077
078 if( !isPotentialClassName( name ) )
079 {
080 throw new InvalidArgumentException(
081 RESOURCES.getMessage(
082 ResourceConstants.CLASSVALIDATOR_BAD_CLASSNAME,
083 name ) );
084 }
085
086 if( m_loadable || m_instance )
087 {
088 final ClassLoader theLoader = getClassLoader();
089
090 try
091 {
092 final Class clazz = theLoader.loadClass( name );
093 if( m_instance )
094 {
095 i.set( clazz.newInstance() );
096 }
097 else
098 {
099 i.set( clazz );
100 }
101 }
102 catch( final ClassNotFoundException exp )
103 {
104 throw new InvalidArgumentException(
105 RESOURCES.getMessage(
106 ResourceConstants.CLASSVALIDATOR_CLASS_NOTFOUND,
107 name ) );
108 }
109 catch( final IllegalAccessException exp )
110 {
111 throw new InvalidArgumentException(
112 RESOURCES.getMessage(
113 ResourceConstants.CLASSVALIDATOR_CLASS_ACCESS,
114 name,
115 exp.getMessage() ) );
116 }
117 catch( final InstantiationException exp )
118 {
119 throw new InvalidArgumentException(
120 RESOURCES.getMessage(
121 ResourceConstants.CLASSVALIDATOR_CLASS_CREATE,
122 name ) );
123 }
124 }
125 }
126 }
127
128 /**
129 * Returns whether the argument value must represent a
130 * class that is loadable.
131 *
132 * @return whether the argument value must represent a
133 * class that is loadable.
134 */
135 public boolean isLoadable()
136 {
137 return m_loadable;
138 }
139
140 /**
141 * Specifies whether the argument value must represent a
142 * class that is loadable.
143 *
144 * @param loadable whether the argument value must
145 * represent a class that is loadable.
146 */
147 public void setLoadable( boolean loadable )
148 {
149 m_loadable = loadable;
150 }
151
152 /**
153 * Returns the {@link ClassLoader} used to resolve and load
154 * the classes specified by the argument values.
155 *
156 * @return the {@link ClassLoader} used to resolve and load
157 * the classes specified by the argument values.
158 */
159 public ClassLoader getClassLoader()
160 {
161 if( m_loader == null )
162 {
163 m_loader = getClass().getClassLoader();
164 }
165 return m_loader;
166 }
167
168 /**
169 * Specifies the {@link ClassLoader} used to resolve and load
170 * the classes specified by the argument values.
171 *
172 * @param loader the {@link ClassLoader} used to resolve and load
173 * the classes specified by the argument values.
174 */
175 public void setClassLoader( ClassLoader loader )
176 {
177 m_loader = loader;
178 }
179
180 /**
181 * Returns whether the argument value must represent a
182 * class that can be instantiated.
183 *
184 * @return whether the argument value must represent a
185 * class that can be instantiated.
186 */
187 public boolean isInstance()
188 {
189 return m_instance;
190 }
191
192 /**
193 * Specifies whether the argument value must represent a
194 * class that can be instantiated.
195 *
196 * @param instance whether the argument value must
197 * represent a class that can be instantiated.
198 */
199 public void setInstance( boolean instance )
200 {
201 m_instance = instance;
202 }
203
204 /**
205 * Returns whether the specified name is allowed as
206 * a Java class name.
207 * @param name the potential classname
208 * @return true if the name is a potential classname
209 */
210 protected boolean isPotentialClassName( final String name )
211 {
212 final char[] chars = name.toCharArray();
213
214 boolean expectingStart = true;
215
216 for( int i = 0; i < chars.length; ++i )
217 {
218 final char c = chars[i];
219
220 if( expectingStart )
221 {
222 if( !Character.isJavaIdentifierStart( c ) )
223 {
224 return false;
225 }
226 expectingStart = false;
227 }
228 else
229 {
230 if( c == '.' )
231 {
232 expectingStart = true;
233 }
234 else if( !Character.isJavaIdentifierPart( c ) )
235 {
236 return false;
237 }
238 }
239 }
240
241 return !expectingStart;
242 }
243 }